<?php
declare(strict_types=1);
namespace Config;
use ArrayAccess;
use Illuminate\Support\Arr;
use Yaf\Application;
use Yaf\Config\Ini;
use Yaf\Exception\TypeError;

class Repository implements ArrayAccess
{
    /**
     * All of the configuration items.
     *
     * @var array
     */
    protected array $items = [];

    /**
     * @throws TypeError
     */
    public function __construct()
    {
        $this->items = $this->init();
    }

    /**
     * @return array
     * @throws TypeError
     */
    private function init(): array
    {
        $config_lst = Application::app()->getConfig()->application->custom->configs;
        if (!$config_lst) {
            $ini = new Ini(CONFIG_PATH . "/common.ini");
            $data = $ini->toArray();
        }else {
            $data = [];
            foreach (explode(",",$config_lst) as $vo) {
                $ini = new Ini(CONFIG_PATH . "/".$vo.".ini");
                $temp = $ini->toArray();
                foreach ($temp as $key=>$vv) {
                    $data[$key] = $vv;
                }
            }
        }
        $file = CONFIG_PATH."common.yaml";
        $config_table = Application::app()->getConfig()->application->system->dbConfigs;
        if (!file_exists($file)&&DB::schema()->hasTable($config_table)) {
            $sysConfig = DB::table($config_table)->get()->toArray();
            $yamlData = [];
            foreach ($sysConfig as $vo) {
                $yamlData[$vo->config_key] = $vo->config_value;
            }
            yaml_emit_file($file,$yamlData);
        }
        if (file_exists($file)){
            $yamlArr = yaml_parse_file($file);
        }else {
            $yamlArr = [];
        }
        return array_merge((array)$data,$yamlArr);
    }


    /**
     * Determine if the given configuration value exists.
     *
     * @param string $key
     * @return bool
     */
    public function has(string $key): bool
    {
        return Arr::has($this->items, $key);
    }

    /**
     * Get many configuration values.
     *
     * @param array $keys
     * @return array
     */
    public function getMany(array $keys): array
    {
        $config = [];
        foreach ($keys as $key => $default) {
            if (is_numeric($key)) {
                [$key, $default] = [$default, null];
            }
            $config[$key] = Arr::get($this->items, $key, $default);
        }
        return $config;
    }

    /**
     * Get the specified configuration value.
     *
     * @param array|string $key
     * @param mixed|null $default
     * @return mixed
     */
    public function get(array|string $key, mixed $default = null): mixed
    {
        if (is_array($key)) {
            return $this->getMany($key);
        }
        return Arr::get($this->items, $key, $default);
    }

    /**
     * Set a given configuration value.
     *
     * @param array|string $key
     * @param mixed|null $value
     * @return void
     */
    public function set(array|string $key, mixed $value = null): void
    {
        $keys = is_array($key) ? $key : [$key => $value];
        foreach ($keys as $key => $value) {
            Arr::set($this->items, $key, $value);
        }
    }

    /**
     * Prepend a value onto an array configuration value.
     *
     * @param string $key
     * @param  mixed  $value
     * @return void
     */
    public function prepend(string $key, mixed $value): void
    {
        $array = $this->get($key, []);

        array_unshift($array, $value);

        $this->set($key, $array);
    }

    /**
     * Push a value onto an array configuration value.
     *
     * @param string $key
     * @param  mixed  $value
     * @return void
     */
    public function push(string $key, mixed $value): void
    {
        $array = $this->get($key, []);

        $array[] = $value;

        $this->set($key, $array);
    }

    /**
     * Get all of the configuration items for the application.
     *
     * @return array
     */
    public function all(): array
    {
        return $this->items;
    }

    /**
     * Determine if the given configuration option exists.
     *
     * @param  string  $key
     * @return bool
     */
    #[ReturnTypeWillChange] public function offsetExists($key): bool
    {
        return $this->has($key);
    }

    /**
     * Get a configuration option.
     *
     * @param  string  $key
     * @return mixed
     */
    #[ReturnTypeWillChange] public function offsetGet($key): mixed
    {
        return $this->get($key);
    }

    /**
     * Set a configuration option.
     *
     * @param  string  $key
     * @param  mixed  $value
     * @return void
     */
    #[ReturnTypeWillChange] public function offsetSet($key, mixed $value): void
    {
        $this->set($key, $value);
    }

    /**
     * Unset a configuration option.
     *
     * @param  string  $key
     * @return void
     */
    #[ReturnTypeWillChange] public function offsetUnset($key): void
    {
        $this->set($key, null);
    }
}
